
#include <BPainter>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BOpenGL>

#include "PixmapArea.h"

PixmapArea::PixmapArea(BWidget* parent)
{
	this->setStyleSheet("background:36, 36, 36");

	this->setAreaAlign(Align_Center);

	_pixmap = new BPixmap();
	_pixmap->setFilter(BTexture::Filter_Nearest);

	_image = new BImage(Format_RGBA);

	_draging = false;
	_zoom = 100;
	_hoveredWidget = 0;

	connect(_pixmap, Signal_Resize, &PixmapArea::slotPixmapResized);
	connect(_pixmap, Signal_Dirty, &PixmapArea::slotPixmapDirty);
	connect(_pixmap, Signal_Frame, &PixmapArea::slotPixmapFrame);

}
PixmapArea::~PixmapArea()
{

}

BPixmap* PixmapArea::pixmap() const
{
	return _pixmap;
}
BImage* PixmapArea::image() const
{
	return _image;
}

ImageLayer* PixmapArea::findLayer(const BImage* image) const
{
	for (int i = 0; i < _pixmap->widgetCount(); i++)
	{
		ImageLayer* layer = (ImageLayer*)_pixmap->widget(i);
		if (layer->image() == image)
			return layer;
	}
	return 0;
}

void PixmapArea::enableEvent()
{
	_eventEnabled = true;
}
void PixmapArea::disableEvent()
{
	_eventEnabled = false;
}
bool PixmapArea::eventEnabled()
{
	return 	_eventEnabled;
}

void PixmapArea::setZoom(int zoom)
{
	zoom = bLimit(zoom, 10, 1000);
	if (_zoom != zoom)
	{
		_zoom = zoom;
		adjustArea();
		this->fresh();
		emit("zoom-changed", _zoom);
	}
}
int PixmapArea::zoom() const
{
	return _zoom;
}

void PixmapArea::setOperation(Operation* operation)
{
	if (_operation != operation)
	{
		if (_operation)
		{
			_operation->setPixmapArea(0);
		}
		_operation = operation;
		if (_operation)
		{
			_operation->setPixmapArea(this);
		}
	}
}
Operation* PixmapArea::operation() const
{
	return _operation;
}

void PixmapArea::setAreaImage(BImage* areaImage)
{
	if (_areaImage != areaImage)
	{
		_areaImage = areaImage;
		this->fresh();
	}
}
BImage* PixmapArea::areaImage() const
{
	return _areaImage;
}

void PixmapArea::select(BWidget* widget)
{
	if (widget && !_selectedWidgets.contain(widget))
	{
		_selectedWidgets.insert(widget);
		this->fresh();
		emit("widget-selected", widget);
	}
}
void PixmapArea::unselect(BWidget* widget)
{
	if (_selectedWidgets.remove(widget))
	{
		this->fresh();
		emit("widget-unselected", widget);
	}
}
void PixmapArea::clearSelection()
{
	if (_selectedWidgets.size())
	{
		while (BWidget* widget = _selectedWidgets.last())
		{
			_selectedWidgets.remove(_selectedWidgets.size() - 1, 1);
			emit("widget-unselected", widget);
		}
		this->fresh();
	}
}
BWidgetSet& PixmapArea::selectedWidgets()
{
	return _selectedWidgets;
}

BRect PixmapArea::mapToPixmap(const BRect& rect) const
{
	double ratio = _zoom / 100.0;
	BRect res = (rect - this->area().pos()) / ratio;
	return res;
}
BRect PixmapArea::mapFromPixmap(const BRect& rect) const
{
	double ratio = _zoom / 100.0;
	BRect res = rect * ratio + this->area().pos();
	return res;
}

BPoint PixmapArea::mapToPixmap(const BPoint& point) const
{
	double ratio = _zoom / 100.0;
	BPoint res = (point - this->area().pos()) / ratio;
	return res;
}
BPoint PixmapArea::mapFromPixmap(const BPoint& point) const
{
	double ratio = _zoom / 100.0;
	BPoint res = point * ratio + this->area().pos();
	return res;
}

void PixmapArea::adjustArea()
{
	if (_pixmap)
	{
		BSize size = _pixmap->size();
		if (_zoom != 100)
		{
			float ratio = _zoom / 100.0f;
			size *= ratio;
		}
		BRect area = this->area();
		if (this->area().size() != size)
		{
			area.setSize(size);
			this->setArea(area);
		}
	}
}

void PixmapArea::slotPixmapResized()
{
	if (_canvas.empty())
	{
		_canvas = new BImage(_pixmap->size(), Format_Gray);
		_canvas->setFilter(BTexture::Filter_Nearest);
	}
	_canvas->setSize(_pixmap->size());
	BByte* data = _canvas->pixels()->data();
	int size = _canvas->pixels()->size();
	for (int r = 0; r < _canvas->height(); r++)
	{
		BByte gray = r % 2 ? 255 : 128;
		BByte back = gray == 255 ? 128 : 255;
		int pos = r * _canvas->width();
		for (int c = 0; c < _canvas->width(); c++)
		{
			if (c % 2)
				data[pos + c] = back;
			else
				data[pos + c] = gray;
		}
	}
	adjustArea();
}
void PixmapArea::slotPixmapDirty()
{
	this->fresh();
}
void PixmapArea::slotPixmapFrame()
{
	_pixmap->capture(_image);
	_image->reverse(Orientation_Vertical);
}

void PixmapArea::scrollArea()
{
	this->fresh();
}

bool PixmapArea::event(const BEvent& event)
{
	if (_eventEnabled)
	{
		double ratio = _zoom / 100.0;
		if (event.type() >= Event_MouseMove && event.type() <= Event_DoubleClick)
		{
			BMouseEvent mouseEvent(event);
			BPoint point = (mouseEvent.pos() - this->area().pos()) * ratio;
			mouseEvent.setGlobalPos(point);
			mouseEvent.setAbsolutePos(point);
			if (_pixmap->event(mouseEvent))
				return true;
		}
		else
		{
			if (_pixmap->event(event))
				return true;
		}
	}
	if (_operation)
	{
		if (_operation->event(event))
			return true;
	}
	if (event.type() == Event_Update || event.type() == Event_Fresh)
	{
		return _pixmap->event(event);
	}
	if (event.type() == Event_MouseWheel)
	{
		return this->mouseWheelEvent(event);
	}
	return BScrollArea::event(event);
}

void PixmapArea::resizeEvent(const BEvent& event)
{
	BSize size = event.value();
	adjustArea();
	BScrollArea::resizeEvent(event);
}

void PixmapArea::freshEvent(const BEvent& event)
{
	BScrollArea::freshEvent(event);
	BRect area = this->area();
	float sw = area.width() / (float)_pixmap->width();
	float sh = area.height() / (float)_pixmap->height();
	_canvas->setEnabled(sw > 4 && sh > 4);
}
void PixmapArea::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawPerch();
	painter.drawMargin();
	painter.drawBorder();

	const BRect& crect = this->centerRect();
	painter.setClip(crect);

	BRect area = this->area();
	painter.setColor(Color_Text);
	painter.drawRect(area);

	if (_canvas->enabled())
	{
		painter.drawImage(this->area(), _canvas);
	}
	else
	{
		painter.setColor(255, 255, 255);
		painter.fillRect(this->area());
	}

	if (_areaImage)
	{
		painter.drawImage(this->area(), _areaImage);
	}

	painter.bind(_pixmap);
	painter.fillRect(this->area());
	painter.unbind();

	float ratio = _zoom / 100.0f;
	if (_hoveredWidget)
	{
		BRect rect = this->mapFromPixmap(_hoveredWidget->rect());
		painter.setLineStipple(0x0F0F);
		painter.setColor(Color_Border);
		painter.drawRect(rect);
		painter.setLineStipple(0);
	}

	if (_selectedWidgets.size())
	{
		for (int i = 0; i < _selectedWidgets.size(); i++)
		{
			BWidget* layer = _selectedWidgets[i];
			BRect rect = layer->rect() * ratio + this->area().pos();
			rect.left() += 1;
			rect.bottom() -= 1;
			painter.setLineStipple(0xFF00);
			painter.setColor(Color_Border_Checked);
			painter.drawRect(rect);
			painter.setLineStipple(0);
		}
	}
	if (_operation)
	{
		_operation->paint(painter);
	}
}

void PixmapArea::keyDownEvent(const BKeyEvent& event)
{
	if (event.key() == Key_Delete)
	{
		if (_selectedWidgets.size())
		{
			//RemoveOpera* opera = new RemoveOpera();
			for (int i = 0; i < _selectedWidgets.size(); i++)
			{
				BWidget* layer = _selectedWidgets[i];
				_pixmap->removeWidget(layer);
				//opera->addLayer(layer);
			}
			//emit("opera", opera);
		}
	}
	if (event.key() == Key_Escape)
	{
		if (_operation)
			emit("operation-escape", _operation);
	}
}

void PixmapArea::mouseEnterEvent(const BMouseEvent& event)
{
	if (_hoveredWidget)
	{
		_hoveredWidget = 0;
		this->fresh();
	}
}
void PixmapArea::mouseLeaveEvent(const BMouseEvent& event)
{
	if (_hoveredWidget)
	{
		_hoveredWidget = 0;
		this->fresh();
	}
}

void PixmapArea::mousePressEvent(const BMouseEvent& event)
{
	if (event.button() == Button_Left)
	{
		if (!event.keyState(Key_LCtrl))
		{
			for(int i = 0; i < _selectedWidgets.size(); i++)
			{
				BWidget* widget = _selectedWidgets[i];
				if (widget == _hoveredWidget)
					continue;
				_selectedWidgets.remove(i--);
				emit("widget-unselected", widget);
			}
		}
		if (_hoveredWidget)
		{
			select(_hoveredWidget);
		}
		this->fresh();
		return;
	}
	if (event.button() == Button_Middle)
	{
		_draging = true;
		_dragPoint = event.pos();
		_dragArea = this->area();
		this->setCursor(Cursor_SizeAll);
	}
}
void PixmapArea::mouseReleaseEvent(const BMouseEvent& event)
{
	if (_draging)
	{
		_draging = false;
		this->setCursor(Cursor_None);
	}
}
void PixmapArea::mouseMoveEvent(const BMouseEvent& event)
{
	const BPoint& pos = event.pos();
	const BRect& area = this->area();
	if (event.button() == Button_Middle)
	{
		BRect area = _dragArea + (pos - _dragPoint);
		this->setArea(area);
		return;
	}
	BPoint point = mapToPixmap(pos);
	emit("global-point", point);
	BWidget* hoveredLayer = (BWidget*)_pixmap->widgetAt(point);
	if (_hoveredWidget != hoveredLayer)
	{
		_hoveredWidget = hoveredLayer;
		this->fresh();
	}
	if (hoveredLayer)
	{
		point -= hoveredLayer->pos();
		emit("layer-point", point);
	}
}
bool PixmapArea::mouseWheelEvent(const BMouseEvent& event)
{
	const BPoint& pos = event.pos();
	BRect area = this->area();
	double xratio = (pos.x() - area.x()) / (double)area.width();
	double yratio = (pos.y() - area.y()) / (double)area.height();
	if (event.wheel() > 0)
	{
		int zoom = _zoom + 10;
		this->setZoom(zoom);
	}
	if (event.wheel() < 0)
	{
		int zoom = _zoom - 10;
		this->setZoom(zoom);
	}
	BRect rect = this->area();
	BPoint point(int(xratio * rect.width() + rect.x()), int(yratio * rect.height() + rect.y()));
	rect.move(pos - point);
	this->setArea(rect);
	return true;
}

